# 网格查找法寻找满足出水达标（≤target_T）的最小PAC单耗

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scipy.stats import pearsonr
from matplotlib.font_manager import FontProperties
from tqdm import tqdm  # 进度条（可选）
from bayes_opt import BayesianOptimization

def grid_search_optimize(
    model, 
    scaler_X, 
    scaler_y, 
    x1_new,          # 新进水浊度（NTU）
    x2_min=0,        # PAC单耗最小值（mg/L）
    x2_max=10,       # PAC单耗最大值（mg/L）
    step=0.5,        # 搜索步长（mg/L）
    target_T=1.0     # 出水浊度达标阈值（NTU）
):
    """
    网格搜索法寻找满足出水达标（≤target_T）的最小PAC单耗
    
    参数：
        model: 训练好的BP模型
        scaler_X: 特征标准化器
        scaler_y: 输出标准化器
        x1_new: 新进水浊度（NTU）
        x2_min: PAC单耗最小值（mg/L）
        x2_max: PAC单耗最大值（mg/L）
        step: 搜索步长（mg/L）
        target_T: 出水达标阈值（NTU）
    
    返回：
        best_x2: 最优PAC单耗（mg/L）
        best_y: 对应出水浊度（NTU）
        all_results: 所有候选值的（x2, y_pred）列表
    """
    # 生成候选PAC单耗列表
    x2_candidates = np.arange(x2_min, x2_max + step, step)#生成等差数列
    all_results = []
    
    # 遍历所有候选值
    for x2 in tqdm(x2_candidates, desc="网格搜索中..."):
        # 构造输入数据（标准化）
        x_input = np.array([[x1_new, x2]])
        x_scaled = scaler_X.transform(x_input)
        
        # 预测出水浊度（反标准化）
        y_pred_scaled = model.predict(x_scaled, verbose=0)
        y_pred = scaler_y.inverse_transform(y_pred_scaled)[0][0]
        
        # 记录结果
        all_results.append((x2, y_pred))
        
        # 提前终止（可选：若找到达标且极小的x2）
        # if y_pred <= target_T and x2 == x2_candidates[0]:
        #     break
    
    # 筛选达标且x2最小的解
    valid_results = [ (x2, y) for x2, y in all_results if y <= target_T ]
    if not valid_results:
        # 无达标解，返回最大x2（需人工干预）
        best_x2 = x2_max
        best_y = scaler_y.inverse_transform(
            model.predict(scaler_X.transform([[x1_new, best_x2]]), verbose=0)
        )[0][0]
        return best_x2, best_y, all_results
    
    # 按x2升序排序，取第一个（最小x2）
    valid_results.sort(key=lambda x: x[0])
    best_x2, best_y = valid_results[0]
    
    return best_x2, best_y, all_results

# ---------------------- 使用示例 ----------------------
# 假设新进水浊度为 8 NTU，达标阈值1 NTU
# x1_new = 8.0
# target_T = 1.0

# # 调用网格搜索
# best_x2_grid, best_y_grid, all_results = grid_search_optimize(
#     model=model,
#     scaler_X=scaler_X,
#     scaler_y=scaler_y,
#     x1_new=x1_new,
#     x2_min=0,
#     x2_max=10,
#     step=0.5,
#     target_T=target_T
# )

# # 输出结果
# print("===== 网格搜索结果 =====")
# print(f"进水浊度 {x1_new} NTU 时，最优PAC单耗（网格搜索）: {best_x2_grid:.2f} mg/L")
# print(f"预测出水浊度: {best_y_grid:.2f} NTU（是否达标：{best_y_grid <= target_T}）")



# ###################################

# 贝叶斯优化法寻找满足出水达标（≤target_T）的最小PAC单耗

def bayesian_optimize(
    model, 
    scaler_X, 
    scaler_y, 
    x1_new,          # 新进水浊度（NTU）
    x2_bounds=(0, 10),  # PAC单耗搜索范围（mg/L）
    target_T=1.0,     # 出水浊度达标阈值（NTU）
    init_points=5,    # 初始随机采样点数量
    n_iter=20         # 贝叶斯优化迭代次数
):
    """
    贝叶斯优化法寻找满足出水达标（≤target_T）的最小PAC单耗
    
    参数：
        model: 训练好的BP模型
        scaler_X: 特征标准化器
        scaler_y: 输出标准化器
        x1_new: 新进水浊度（NTU）
        x2_bounds: PAC单耗搜索范围（min, max）
        target_T: 出水达标阈值（NTU）
        init_points: 初始随机采样点数量
        n_iter: 贝叶斯优化迭代次数
    
    返回：
        best_x2: 最优PAC单耗（mg/L）
        best_y: 对应出水浊度（NTU）
    """
    # 定义目标函数（最小化PAC单耗，同时满足出水达标）
    def objective(x2):
        # 构造输入数据（标准化）
        x_input = np.array([[x1_new, x2]])
        x_scaled = scaler_X.transform(x_input)
        
        # 预测出水浊度（反标准化）
        y_pred_scaled = model.predict(x_scaled, verbose=0)
        y_pred = scaler_y.inverse_transform(y_pred_scaled)[0][0]
        
        # 惩罚未达标的情况（未达标时目标函数值极大）
        if y_pred > target_T:
            return -1e6  # 越大越好，取负号转为最小化
        else:
            return x2  # 最小化x2
    
    # 初始化贝叶斯优化器
    optimizer = BayesianOptimization(
        f=objective,
        pbounds={'x2': x2_bounds},
        random_state=42
    )
    
    # 执行优化
    optimizer.maximize(init_points=init_points, n_iter=n_iter)
    
    # 提取最优解
    best_params = optimizer.max['params']
    best_x2 = best_params['x2']
    
    # 计算对应出水浊度
    x_input = np.array([[x1_new, best_x2]])
    x_scaled = scaler_X.transform(x_input)
    y_pred_scaled = model.predict(x_scaled, verbose=0)
    best_y = scaler_y.inverse_transform(y_pred_scaled)[0][0]
    
    return best_x2, best_y

# ---------------------- 使用示例 ----------------------
# x1_new = 8.0
# target_T = 1.0

# # 调用贝叶斯优化
# best_x2_bayes, best_y_bayes = bayesian_optimize(
#     model=model,
#     scaler_X=scaler_X,
#     scaler_y=scaler_y,
#     x1_new=x1_new,
#     x2_bounds=(0, 10),
#     target_T=target_T,
#     init_points=5,
#     n_iter=20
# )

# # 输出结果
# print("===== 贝叶斯优化结果 =====")
# print(f"进水浊度 {x1_new} NTU 时，最优PAC单耗（贝叶斯优化）: {best_x2_bayes:.2f} mg/L")
# print(f"预测出水浊度: {best_y_bayes:.2f} NTU（是否达标：{best_y_bayes <= target_T}）")




# ###################################
